package abel.wlmr.model.unit;

import java.util.ArrayList;
import java.util.List;

import abel.wlmr.model.WlmrBasePart;
import abel.wlmr.model.WlmrBaseUnit;
import abel.wlmr.model.WlmrDefaultUnit;
import abel.wlmr.model.WlmrException;
import abel.wlmr.model.type.DataIdBody;
import io.netty.buffer.ByteBuf;

/**
 * 数据单元标识 1
 * 数据单元 1
 * ……
 * 数据单元标识 n
 * 数据单元 n
 * PW（见本附录 B.4.3.4.6.2）
 * Tp（见本附录 B.4.3.4.6.4）
 * 
 * B.5.4.1.3.18 F65：定时上报1类数据任务设置
 * pn 表示 1～64 个任务序号，数据单元格式见表 B.32。JG/T 162—XXXX
 * 63
 * 表B.32 定时上报 1 类数据任务设置数据单元格式
 * 数据内容
 * 数据格式 字节数 说明
 * D7 D6 D5 D4 D3 D2 D1 D0
 * 定时上报周期单位 定时上报周期 BIN 1
 * 上报基准时间：秒分时日月年 见附录 C.1 6
 * 曲线数据抽取倍率 R BIN 1
 * 数据单元标识个数 n BIN 1
 * 数据单元标识 1
 * 见本附录
 * B.5.12.1.2
 * 4 在请求 1 类数
 * 据报文规定的
 * 数据单元标识
 * 中选取。
 * ……
 * 数据单元标识 n 4
 * ——定时上报周期单位：
 * 用 D6～D7 编码表示，取值 0～3 依次表示分、时、日、月。
 * ——定时上报周期：
 * 用 D0～D5 表示，为定时上报数据的时间周期。
 * ——上报基准时间：
 * 终端以此设定的时间开始上报，并按定时上报周期循环。
 * ——曲线抽取数据倍率 R：取值范围 1～96，
 * 表示终端按此倍率抽取数据上送，如被抽取的数据的冻结密度 m=2，即每 30 分钟冻结一个值，那
 * 么当 R=2 时，表示按 60 分钟抽取， R=1 时，表示仍按 30 分钟抽取。
 * ——数据单元标识个数 n：
 * 表示本任务具有的数据单元标识个数。
 * B.5.4.1.3.19 F66：定时上报2类数据任务设置
 * pn 表示 1～64 个任务序号，数据单元格式见表 B.33。
 * 表B.33 定时上报 2 类数据任务设置数据单元格式
 * 数据内容
 * 数据格式 字节
 * D7 D6 D5 D4 D3 D2 D1 D0 数 说明
 * 定时上报周期单
 * 位 定时上报周期 BIN 1 同本附录 B.5.4.1.3.18
 * 上报基准时间：秒分时日月年 见附录
 * C.1 6 同本附录 B.5.4.1.3.18
 * 曲线数据抽取倍率 R BIN 1 同本附录 B.5.4.1.3.18
 * 数据单元标识个数 n BIN 1 同本附录 B.5.4.1.3.18
 * 数据单元标识 1
 * 见附录
 * B.5.13.1.2
 * 4
 * 在请求 2 类数据报文规定的数据单元标
 * …… 识中选取。
 * 数据单元标识 n 4
 * 根据定时上报 2 类数据任务设置：JG/T 162—XXXX
 * 64
 * ——在自动上送日冻结数据时，上送的仅是前一日的日冻结数据。
 * ——在自动上送月冻结数据时，上送的仅是前一月的月冻结数据。
 * B.5.4.1.3.20 F67：定时上报1类数据任务启动/停止设置
 * 数据单元格式见表 B.34。
 * 表B.34 定时上报 1 类数据任务启动/停止设置数据单元格式
 * 数据内容 数据格式 字节数
 * 启动/停止标志 BIN 1
 * ——置“55H”：启动；置“AAH” 停止；其它数值无效。
 * B.5.4.1.3.21 F68：定时上报2类数据任务启动/停止设置
 * 数据单元格式同本附录 B.5.4.1.3.20
 * 
 * @author Abel
 *
 */
public abstract class Afn04Part extends WlmrBasePart {

    public static final short F_FIRST_RPT = 65;
    public static final short F_SECOND_RPT = 66;
    public static final short F_FIRST_START = 67;
    public static final short F_SECOND_START = 68;

    public static final short F_METER_CONFIG = 401;
    public static final short F_EXCEPTION = 403;
    public static final short F_SETTING = 405;

    public Afn04Part() {
        super();
    }

    public Afn04Part(short guessedLen) {
        super(guessedLen);
    }

    /* (non-Javadoc)
     * @see abel.wlmr.model.AppUnitPart#parseBuf(io.netty.buffer.ByteBuf)
     */
    @Override
    public int parseBuf(ByteBuf buf) throws WlmrException {
        if (getGuessedLength() < DataIdBody.FIXED_LEN) {
            throw new WlmrException("not enough bytes for data id.");
        }

        id = new DataIdBody();
        id.parseBuf(buf);
        short len = (short) (getGuessedLength() - DataIdBody.FIXED_LEN);

        for (short da : id.getDa().addrList()) {
            // same DA.
            List<WlmrBaseUnit> units = new ArrayList<>();
            unitList.add(units);

            // DT list for one DA.
            for (short dt : id.getDt().typeList()) {
                DataIdBody outerId = new DataIdBody();
                outerId.getDa().setAddrList(da);
                outerId.getDt().setTypeList(dt);

                switch (dt) {
                case F_FIRST_RPT:
                case F_SECOND_RPT:

                {
                    Afn04F6566Unit unit = new Afn04F6566Unit(len);
                    unit.setOuterId(outerId);

                    int plen = unit.parseBuf(buf);
                    if (plen < 0) {
                        throw new WlmrException("not enough bytes for 04-65_66");
                    }
                    len -= plen;

                    units.add(unit);

                    break;
                }

                case F_FIRST_START:
                case F_SECOND_START:

                {
                    Afn04F6768Unit unit = new Afn04F6768Unit(len);
                    unit.setOuterId(outerId);

                    int plen = unit.parseBuf(buf);
                    if (plen < 0) {
                        throw new WlmrException("not enough bytes for 04-67_68");
                    }
                    len -= plen;

                    units.add(unit);

                    break;
                }

                case F_METER_CONFIG:

                {
                    Afn04F401Unit unit = new Afn04F401Unit(len);
                    unit.setOuterId(outerId);

                    int plen = unit.parseBuf(buf);
                    if (plen < 0) {
                        throw new WlmrException("not enough bytes for 04-401");
                    }
                    len -= plen;

                    units.add(unit);

                    break;
                }

                case F_EXCEPTION:

                {
                    Afn04F403Unit unit = new Afn04F403Unit(len);
                    unit.setOuterId(outerId);

                    int plen = unit.parseBuf(buf);
                    if (plen < 0) {
                        throw new WlmrException("not enough bytes for 04-403");
                    }
                    len -= plen;

                    units.add(unit);

                    break;
                }

                case F_SETTING:

                {
                    Afn04F405Unit unit = new Afn04F405Unit(len);
                    unit.setOuterId(outerId);

                    int plen = unit.parseBuf(buf);
                    if (plen < 0) {
                        throw new WlmrException("not enough bytes for 04-405");
                    }
                    len -= plen;

                    units.add(unit);

                    break;
                }

                default:

                {
                    WlmrBaseUnit body = new WlmrDefaultUnit(len);
                    body.setOuterId(outerId);

                    len -= body.parseBuf(buf);

                    units.add(body);
                    break;
                }
                }
            }
        }

        return getGuessedLength();
    }

}
